import os
import fitz  # PyMuPDF
from PIL import Image
import io
import base64
import re
import zipfile
import tempfile
from utils import log_info, log_exception, log_error, log_warning, log_debug

import concurrent.futures
from services.importers.base_importer import BaseImporter

class PDFImporter(BaseImporter):
    def __init__(self):
        super().__init__()
        # 特殊处理映射
        self.special_mapping = {'LK060': 'LK0603'}
        # 题目字段顺序
        self.field_order = ['J', 'P', 'I', 'Q', 'T', 'A', 'B', 'C', 'D', 'F', 'LA', 'LB', 'LC', 'type']
        # 所需PDF文件列表
        self.required_pdfs = {
            'total': '总题库.pdf',
            'images': '总题库附图标记.pdf',
            'a': 'A类题库.pdf',
            'b': 'B类题库.pdf',
            'c': 'C类题库.pdf'
        }
        # 初始化统计信息
        self.stats = {
            'single_choice': 0,  # 单选题总数
            'multiple_choice': 0,  # 多选题总数
            'type_a': 0,  # A类题总数
            'type_b': 0,  # B类题总数
            'type_c': 0,  # C类题总数
            'has_image': 0,  # 含图片的题目总数
            'single_choice_type_a': 0,  # A类单选题
            'single_choice_type_b': 0,  # B类单选题
            'single_choice_type_c': 0,  # C类单选题
            'multiple_choice_type_a': 0,  # A类多选题
            'multiple_choice_type_b': 0,  # B类多选题
            'multiple_choice_type_c': 0,  # C类多选题
            'image_question_type_a': 0,  # A类含图片题目
            'image_question_type_b': 0,  # B类含图片题目
            'image_question_type_c': 0   # C类含图片题目
        }

    def read_pdf_text(self, pdf_path):
        """读取PDF文件文本内容（使用PyMuPDF优化）"""
        text_content = []
        try:
            doc = fitz.open(pdf_path)
            for page_num in range(len(doc)):
                page = doc[page_num]
                text_content.append(page.get_text())
            doc.close()
        except Exception as e:
            log_error(f"读取PDF文本时出错: {e}")

        return '\n'.join(text_content)

    def extract_images_from_pdf(self, pdf_path):
        """从PDF中提取图片并返回图片J值与Base64编码的映射（确保图片顺序正确）"""
        images_map = {} 
        try:
            # 读取图片标记PDF的文本内容，获取图片对应的J值
            text_content = self.read_pdf_text(pdf_path)
            # 提取所有J值（假设格式为空格分隔的J值列表）
            img_j_values = [j.strip() for j in text_content.split() if j.strip()]
            img_index = 0

            # 使用PyMuPDF打开PDF
            doc = fitz.open(pdf_path)
            for page_num in range(len(doc)):
                page = doc[page_num]
                # 获取页面上的所有图片
                image_list = page.get_images(full=True)
                
                # 确保图片按文档顺序处理
                for img in sorted(image_list, key=lambda x: x[0]):  # 按xref排序，确保顺序稳定
                    if img_index >= len(img_j_values):
                        break

                    # 获取图片数据
                    xref = img[0]
                    base_image = doc.extract_image(xref)
                    img_data = base_image["image"]
                    
                    try:
                        # 尝试打开图片
                        img = Image.open(io.BytesIO(img_data))

                        # 获取对应的J值
                        j_value = img_j_values[img_index]
                        # 特殊处理
                        j_value = self.special_mapping.get(j_value, j_value)

                        # 转换为Base64
                        buffered = io.BytesIO()
                        img.save(buffered, format="PNG")
                        img_base64 = base64.b64encode(buffered.getvalue()).decode('utf-8')
                        images_map[j_value] = f"data:image/png;base64,{img_base64}"
                        img_index += 1
                    except Exception as img_err:
                        log_warning(f"处理图片 {img_index} 时出错: {img_err}")
                        continue
            doc.close()
        except Exception as e:
            log_error(f"提取图片时出错: {e}")
        return images_map

    def parse_questions_from_text(self, text_content, images_map=None):
        """从文本内容解析题目数据"""
        questions = []
        current_question = {}
        last_field = None

        # 预处理文本内容，忽略页码行
        lines = text_content.split('\n')
        filtered_lines = []
        for line in lines:
            # 去除所有空白字符（空格、制表符、换行符、回车符等）
            clean_line = re.sub(r'\s+', '', line)
            # 忽略页码行：仅纯数字行
            is_page_number = clean_line.isdigit()
            if clean_line and not is_page_number:
                filtered_lines.append(line)
        filtered_text = '\n'.join(filtered_lines)
        # log_info(f"过滤后的文本内容: {filtered_text}")
        # 使用正则表达式匹配字段和值
        # 增强匹配[X]、X]等格式，X限定为JPIQTFABCD
        # 重新设计正则表达式，确保能正确匹配所有格式的字段，特别是以X]开头的选项
        pattern = r'(?:\[(?:[JPIQTFABCD])\]|[JPIQTFABCD]\])\s*([\s\S]*?)(?=\n*(?:\[(?:[JPIQTFABCD])\]|[JPIQTFABCD]\])|$)'  # 分别匹配[X]和X]两种格式，确保每个字段都能被正确识别
        matches = re.finditer(pattern, filtered_text, re.DOTALL)
        
        # 为了更好地理解匹配问题，记录原始文本的部分内容
        # if len(filtered_text) > 200:
        #     log_info(f"原始文本预览 (前200字符): {filtered_text[:200]}...")

        # 调试信息：显示匹配的字段和值
        all_matches = list(matches)
        # if len(all_matches) > 0:
        #     log_info(f"总共匹配到 {len(all_matches)} 个字段")
        #     # 显示前几个匹配的字段，帮助调试
        #     for i, match in enumerate(all_matches):
        #         # 从匹配的整个字符串中提取字段名
        #         full_match = match.group(0)
        #         # 提取字段名（J、P、I、Q、T、F、A、B、C、D中的一个）
        #         field_match = re.search(r'\[?([JPIQTFABCD])\]', full_match)
        #         field = field_match.group(1) if field_match else 'UNKNOWN'
        #         value = match.group(1).strip()
        #         log_info(f"匹配 {i+1}: 字段={field}, 值={value}, +++")
        matches = iter(all_matches)  # 重新创建迭代器

        # 通用字段清理函数
        def clean_field_value(value):
            # 去除首尾空白字符（包括换行符）
            stripped_value = value.strip()
            # 去除值末尾的页码信息（数字和空白字符）
            no_page_value = re.sub(r'\s+\d+\s*$', '', stripped_value)
            # 完全去除内部的换行符
            no_newline_value = re.sub(r'[\r\n]+', '', no_page_value)
            return no_newline_value

        for match in matches:
            # 从匹配的整个字符串中提取字段名
            full_match = match.group(0)
            # 提取字段名（J、P、I、Q、T、F、A、B、C、D中的一个）
            field_match = re.search(r'\[?([JPIQTFABCD])\]', full_match)
            field = field_match.group(1) if field_match else 'UNKNOWN'
            value = match.group(1).strip()

            # 检查是否是同一行中的多个J字段
            # 假设同一行中的J字段后面不会跟着其他字段
            is_same_line_j = field == 'J' and current_question and 'J' in current_question and len(current_question) == 1

            # if current_question.get('J') == 'LK0517':
            #     # log_info(f"匹配 : 字段={field}, 值={value}")
            #     if field == 'T':
            #         log_info(f"T内容: {value}")

            # 清理字段值
            clean_value = clean_field_value(value)

            if field == 'J':
                if is_same_line_j:
                    # 同一行中的多个J字段
                    current_question['J'] = f"{current_question['J']}{clean_value}"
                elif current_question:
                    # 遇到新的J字段，保存当前题目并开始新题目
                    questions.append(current_question)
                    current_question = {'J': clean_value}
                else:
                    # 第一个J字段
                    current_question['J'] = clean_value
            else:
                # 正常处理字段
                current_question[field] = clean_value
            last_field = field

        # 添加最后一个题目
        if current_question:
            questions.append(current_question)

        # 添加图片数据
        if images_map:
            for q in questions:
                j_value = q.get('J')
                if j_value:
                    # 处理可能的文件名格式J值（如去掉扩展名）
                    clean_j_value = j_value.split('.')[0] if '.' in j_value else j_value
                    # 检查原始J值
                    if j_value in images_map:
                        q['F'] = images_map[j_value]
                    # 检查清理后的J值
                    elif clean_j_value in images_map:
                        q['F'] = images_map[clean_j_value]
                    # 检查特殊映射
                    elif clean_j_value in self.special_mapping and self.special_mapping[clean_j_value] in images_map:
                        mapped_j = self.special_mapping[clean_j_value]
                        q['F'] = images_map[mapped_j]
                    else:
                        # 确保F字段始终存在，即使没有图片
                        q['F'] = ''
        # 确保所有题目都有F字段
        for q in questions:
            if 'F' not in q or not q['F']:
                q['F'] = ''

        return questions

    def process_category_pdf(self, pdf_path, category):
        """处理分类题库PDF，返回该分类下的题目I值列表"""
        text_content = self.read_pdf_text(pdf_path)
        # 提取所有I字段值
        pattern = r'\[I\]\s*(.*?)\s*(?=\[|$)'
        i_values = re.findall(pattern, text_content)
        return i_values

    def check_zip_contents(self, zip_path):
        """检查ZIP文件内容是否包含所有必需的PDF文件"""
        try:
            with zipfile.ZipFile(zip_path, 'r', metadata_encoding='utf-8') as zip_ref:
                log_debug(f"ZIP文件内容: {zip_ref.namelist()}")
                zip_contents = zip_ref.namelist()
                # 过滤掉__MACOSX目录中的文件
                filtered_contents = [item for item in zip_contents if '__MACOSX' not in item]
                log_debug(f"过滤后的ZIP文件内容: {filtered_contents}")
                # 检查是否包含所有必需的PDF文件
                missing_files = []
                log_debug(f"必需的PDF文件: {self.required_pdfs.items()}")
                for key, filename in self.required_pdfs.items():
                    log_debug(f"检查文件: {filename}")
                    if not any(filename.lower() in item.lower() for item in filtered_contents):
                        log_debug(f"文件 {filename} 未找到")
                        missing_files.append(filename)
                return missing_files
        except Exception as e:
            log_error(f"检查ZIP文件时出错: {e}")
            return ["ZIP文件无效或已损坏"]

    def read_file(self, zip_file_path, progress_var=None, progress_label=None, import_dialog=None):
        """实现基类的抽象方法，读取并解析PDF文件"""
        try:
            if progress_label and import_dialog:
                progress_label.config(text="正在检查ZIP文件内容...")
                log_debug(f"正在检查ZIP文件内容: {zip_file_path}")
                import_dialog.update_idletasks()

            # 检查ZIP文件内容
            missing_files = self.check_zip_contents(zip_file_path)
            if missing_files:
                message = f"ZIP文件中缺少以下必需的PDF文件：\n{', '.join(missing_files)}"
                log_debug(message)
                return False, message

            if progress_label and import_dialog:
                progress_label.config(text="正在解压ZIP文件...")
                log_debug(f"正在解压ZIP文件: {zip_file_path}")
                import_dialog.update_idletasks()

            # 创建临时目录
            with tempfile.TemporaryDirectory() as temp_dir:
                # 解压ZIP文件
                with zipfile.ZipFile(zip_file_path, 'r', metadata_encoding='utf-8') as zip_ref:
                    zip_ref.extractall(temp_dir)

                if progress_label and import_dialog:
                    progress_label.config(text="正在查找PDF文件...")
                    log_debug(f"正在查找PDF文件: {temp_dir}")
                    import_dialog.update_idletasks()

                # 查找解压后的PDF文件
                pdf_files = {}
                for root, _, files in os.walk(temp_dir):
                    # 忽略__MACOSX目录
                    if '__MACOSX' in root:
                        continue
                    for file in files:
                        file_lower = file.lower()
                        if file_lower.endswith('.pdf'):
                            for key, required_file in self.required_pdfs.items():
                                if required_file.lower() in file_lower and key not in pdf_files:
                                    pdf_files[key] = os.path.join(root, file)

                # 确保所有PDF文件都找到了
                if len(pdf_files) < len(self.required_pdfs):
                    missing = [v for k, v in self.required_pdfs.items() if k not in pdf_files]
                    message = f"无法在ZIP中找到所有必需的PDF文件：\n{', '.join(missing)}"
                    log_debug(message)
                    return False, message

                if progress_label and import_dialog:
                    progress_label.config(text="正在读取总题库文件...")
                    log_debug(f"正在读取总题库文件: {pdf_files['total']}")
                    import_dialog.update_idletasks()

                # 读取总题库文本内容
                text_content = self.read_pdf_text(pdf_files['total'])

                if progress_label and import_dialog:
                    progress_label.config(text="正在提取图片...")
                    log_debug(f"正在提取图片: {pdf_files['images']}")
                    import_dialog.update_idletasks()

                # 提取图片
                images_map = self.extract_images_from_pdf(pdf_files['images'])

                if progress_label and import_dialog:
                    progress_label.config(text="正在解析题目...")
                    log_debug(f"正在解析题目: {pdf_files['total']}")
                    import_dialog.update_idletasks()

                # 解析题目
                questions = self.parse_questions_from_text(text_content, images_map)
                total = len(questions)
                if total == 0:
                    return False, "题库文件为空"

                if progress_label and import_dialog:
                    progress_label.config(text="正在读取分类题库...")
                    log_debug(f"正在读取分类题库: {pdf_files['a']}, {pdf_files['b']}, {pdf_files['c']}")
                    import_dialog.update_idletasks()

                # 读取分类题库 - 使用并行处理
                log_debug(f"开始并行处理分类题库...")
                with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
                    # 提交任务
                    future_a = executor.submit(self.process_category_pdf, pdf_files['a'], 'A')
                    future_b = executor.submit(self.process_category_pdf, pdf_files['b'], 'B')
                    future_c = executor.submit(self.process_category_pdf, pdf_files['c'], 'C')
                    
                    # 获取结果
                    a_i_values = future_a.result()
                    b_i_values = future_b.result()
                    c_i_values = future_c.result()
                log_debug(f"分类题库并行处理完成")

                # 处理题目分类
                processed_questions = []
                for q in questions:
                    # 检查必要字段（J、P、I、Q、T、A、B、C、D为必选，F为可选）
                    required_fields = ['J', 'P', 'I', 'Q', 'T', 'A', 'B', 'C', 'D']
                    
                    # 对于LK0517题目，显示详细的调试信息
                    j_value = q.get('J', '')
                    # if j_value == 'LK0517':
                    #     log_info(f"调试 LK0517 题目: 包含的字段={list(q.keys())}")
                    #     for field, value in q.items():
                    #         log_info(f"  字段 {field}: {value}")
                    
                    if not all(key in q for key in required_fields):
                        missing_fields = [key for key in required_fields if key not in q]
                        log_warning(f"跳过题目 (J={j_value}): 缺少必要字段 {missing_fields}")
                        continue
                    else:
                        # 对于LK0517题目，记录处理成功的信息
                        if j_value == 'LK0517':
                            log_debug(f"LK0517 题目处理成功！包含所有必要字段。")

                    # 处理分类
                    i_value = q.get('I', '')
                    la = 1 if i_value in a_i_values else 0
                    lb = 1 if i_value in b_i_values else 0
                    lc = 1 if i_value in c_i_values else 0

                    # 处理空值
                    for key in self.field_order:
                        if key not in q or q[key] is None:
                            q[key] = ''

                    # 根据答案字段(T)的长度设置题型
                    t_value = q.get('T', '')
                    if len(t_value) == 1:
                        question_type = 1  # 单选题
                    elif len(t_value) > 1:
                        question_type = 2  # 多选题
                    else:
                        question_type = 0  # 默认为0

                    # 添加到处理后的题目列表
                    processed_q = {
                        'J': q.get('J', ''),
                        'P': q.get('P', ''),
                        'I': i_value,
                        'Q': q.get('Q', ''),
                        'T': t_value,
                        'A': q.get('A', ''),
                        'B': q.get('B', ''),
                        'C': q.get('C', ''),
                        'D': q.get('D', ''),
                        'F': q.get('F', ''),
                        'LA': la,
                        'LB': lb,
                        'LC': lc,
                        'type': question_type
                    }
                    processed_questions.append(processed_q)

                return processed_questions
        except Exception as e:
            log_exception(f"读取PDF文件时发生异常: {str(e)}")
            return []

import threading

def process_pdf_data(file_path, progress_var=None, progress_label=None, import_dialog=None):
    """处理PDF数据并导入数据库"""
    importer = PDFImporter()
    return importer.process_data(file_path, progress_var, progress_label, import_dialog)
